package cn.datawin.spider.httputil;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.zip.GZIPOutputStream;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;

import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.StatusLine;
import org.apache.http.client.CookieStore;
import org.apache.http.client.entity.GzipDecompressingEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.mime.FormBodyPart;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HttpContext;

import cn.datawin.spider.httputil.HttpRequest;
import cn.datawin.spider.httputil.HttpRequest.Method;
import cn.datawin.spider.httputil.HttpResponse;
import cn.datawin.spider.httputil.HttpService;
import cn.datawin.spider.util.IOUtil;

@SuppressWarnings("deprecation")
public class HttpClientGZipService extends DefaultHttpClient implements HttpService{
	
	int state = 0;
	
	public HttpResponse execute(HttpRequest req) throws Exception {
		long start = System.currentTimeMillis();
		HttpRequestBase requestBase = null;
		org.apache.http.HttpResponse httpResponse = null;
		try {
			initParams(req);

			requestBase = getHttpRequestBase(req);
			httpResponse = this.execute(requestBase);

			StatusLine line = httpResponse.getStatusLine();

			HttpResponse res = new HttpResponse(line.getStatusCode(), line.getReasonPhrase());

			/* 返回头文件 */
			for (Header header : httpResponse.getAllHeaders()) {
				if ("Set-Cookie".equals(header.getName()) && res.getHeader("Set-Cookie") != null) {
					res.setHeader(header.getName(), res.getHeader("Set-Cookie") + "," + header.getValue());
					continue;
				}
				res.setHeader(header.getName(), header.getValue());
			}

			/* 返回cookies */
			for (Cookie co : this.getCookieStore().getCookies()) {
				res.setCookie(co.getName(), co.getValue());
			}

			ByteArrayOutputStream outputStream =  new ByteArrayOutputStream();
			
			InputStream in = httpResponse.getEntity().getContent();
			IOUtil.copy(in, outputStream);
			
			/*返回流 byte[] ,返回流*/
			res.setResponseData(outputStream.toByteArray());
			return res;
		} catch(Exception e){
			System.out.println("HttpClientService=="+req.getUrl());
			throw e;
		}finally {
			requestBase.reset();
			httpResponse = null;
			long l = (System.currentTimeMillis()-start);
			System.out.println(l+"::URL::" +req.getUrl());
		}
	}
	
	public HttpClientGZipService() {
		super();
		bindInterceptor();
	}
	
	public HttpClientGZipService (ClientConnectionManager conman, HttpParams params){
		super(conman, params);
		bindInterceptor();
	}
	
	public HttpClientGZipService (ClientConnectionManager conman){
		super(conman);
		bindInterceptor();
	}
	
	public void bindInterceptor(){
		this.addRequestInterceptor(new HttpRequestInterceptor() {
			@Override
			public void process(org.apache.http.HttpRequest req, HttpContext arg1) throws HttpException, IOException {
				if(!req.containsHeader("Accept-Encoding")){
					req.addHeader("Accept-Encoding", "gzip,deflate,sdch");
				}
			}
		});
		
		this.addResponseInterceptor(new HttpResponseInterceptor() {
			@Override
			public void process(org.apache.http.HttpResponse resp, HttpContext arg1) throws HttpException, IOException {
				Header[] headers = resp.getHeaders("Content-Encoding");
				for(Header header: headers){
					if("gzip".equals(header.getValue())){
						resp.setEntity(new GzipDecompressingEntity(
								resp.getEntity()));
						return;
					}
				}
			}
		});
	}	
	
	private void initParams(HttpRequest req) {
		 HttpParams params = this.getParams();
         params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, req.getConnectTimeout());
         params.setParameter(CoreConnectionPNames.SO_TIMEOUT, req.getReadTimeout());
         params.setParameter(ClientPNames.HANDLE_REDIRECTS, true);
         params.setParameter("http.protocol.cookie-policy", CookiePolicy.BEST_MATCH);
         params.setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, true);  
         HttpProtocolParams.setUseExpectContinue(params, false);
         this.setParams(params);
	}
	
	/**
	 *  解析httpRequest
	 * @param req
	 * @return
	 */
	public HttpRequestBase getHttpRequestBase(HttpRequest req) {
		HttpRequestBase requestBase =null;
		if(req.getMethod()==Method.post){
			requestBase = new HttpPost(req.getUrl());
			
			if(req.getFileMap().size()>0){
				parseMartipart(requestBase, req);
			}else if(req.getPostParams().size()>0){
				parsePostParams(requestBase, req);
			}
		}else{
			if (req.getPostParams().size()>0) {
				String spe = req.getUrl().indexOf("?") != -1 ? "&" : "?";
				requestBase = new HttpGet(req.getUrl() + spe + req.postParamsToString());
			}else{
				requestBase = new HttpGet(req.getUrl());
			}
		}
		
		parseHeader(requestBase, req);
		parseCookies(requestBase, req);
		
		return requestBase;
	}
	
	/**
	 * 解析头文件
	 * @param requestBase
	 * @param req
	 */
	public void parseHeader(HttpRequestBase requestBase, HttpRequest req){
		Map<String, String> headers = req.getHeaders();
		for(String key: headers.keySet()){
			requestBase.setHeader(key, headers.get(key));
		}
	}
	
	/**
	 * 解析cookie
	 * @param requestBase
	 * @param req
	 */
	public void parseCookies(HttpRequestBase requestBase, HttpRequest req){
		Map<String, String> cookies = req.getCookies();
		CookieStore cookieStore = this.getCookieStore();
		for(String key: cookies.keySet()){
			cookieStore.addCookie(new BasicClientCookie(key, cookies.get(key)));
		}
	}
	
	/**
	 * 解析提交参数
	 * @param requestBase
	 * @param req
	 */
	public void parsePostParams(HttpRequestBase requestBase, HttpRequest req){
		byte [] bgzip = gzip(req.postParamsToJson());
		((HttpPost)requestBase).setEntity(new InputStreamEntity(new ByteArrayInputStream(bgzip), bgzip.length));
	}
	
	public byte[] gzip(String foo){
	    ByteArrayOutputStream baos = new ByteArrayOutputStream();
	    GZIPOutputStream gzos = null;

	    try {
	        gzos = new GZIPOutputStream(baos);
	        gzos.write(foo.getBytes("UTF-8"));
	        gzos.flush();
	    } catch (IOException e) {
	        e.printStackTrace();
	    } finally {
	        if (gzos != null) try { gzos.close(); } catch (IOException ignore) {};
	    }

	    return baos.toByteArray();
	}
	
	/**
	 * 解析文件流
	 * @param requestBase
	 * @param req
	 */
	public void parseMartipart(HttpRequestBase requestBase,HttpRequest req){
		MultipartEntity entity = new MultipartEntity();
		String charset = req.getCharset();
		
		Map<String, String> postMap = req.getPostParams();
		for (String key : postMap.keySet()) {
			String value = postMap.get(key);
			value = value==null ? "" : value;
			try {
				entity.addPart(key, new StringBody(value, Charset.forName(charset)));
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
		}
		
		Map<String, File> fileMap = req.getFileMap();
		for (String key : fileMap.keySet()) {
			File value = fileMap.get(key);
			entity.addPart(new FormBodyPart(key, new FileBody(value, getMimeType(value))));
		}
		((HttpPost)requestBase).setEntity(entity);;
	}
	
	private String getMimeType(File file){
		 FileNameMap fileNameMap = URLConnection.getFileNameMap();  
	     return fileNameMap.getContentTypeFor(file.toString());  
	}
	
	public static ClientConnectionManager initSSH(){
		try{
			ClientConnectionManager ccm =  new ThreadSafeClientConnManager();  
		    SSLContext sslcontext = SSLContext.getInstance("TLS");
		    sslcontext.init(null, new TrustManager[]{new X509TrustManager()}, null);
		    SSLSocketFactory ssf = new SSLSocketFactory(sslcontext);
		    SchemeRegistry sr = ccm.getSchemeRegistry();
		    sr.register(new Scheme("https", 443, ssf));
		    return ccm;
		}catch(Exception e){
			return null;
		}
	}
	
	public static class X509TrustManager implements javax.net.ssl.X509TrustManager{
		 public void checkClientTrusted(X509Certificate[] arg0,
                 String arg1) throws CertificateException {
         }

         public void checkServerTrusted(X509Certificate[] arg0,
                 String arg1) throws CertificateException {
         }

         public X509Certificate[] getAcceptedIssuers() {
             return null;
         }
	}

	@Override
	public void shutdown() {
		state = 1;
		super.getConnectionManager().shutdown();
		System.out.println("client 已释放");
	}

	@Override
	public boolean isShutdown() {
		if(state == 1) return true;
		return false;
	}
	
}
